// $Id: config_file.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

//
// Copyright notice reproduced from
// <boost/program_options/detail/config_file.hpp>,
// <boost/program_options/detail/parsers.hpp>,
// from which this code was originally taken.
//
// Modified by Joseph Wu to fits the library.
//

#ifndef XPTPLUS_CONFIG_FILE_HPP
#define XPTPLUS_CONFIG_FILE_HPP

#include <iosfwd>
#include <string>
#include <set>

#include <boost/noncopyable.hpp>
#include <boost/program_options/config.hpp>
#include <boost/program_options/option.hpp>
#include <boost/program_options/eof_iterator.hpp>
#include <boost/program_options/parsers.hpp>

#include <boost/detail/workaround.hpp>
#if BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3202))
#include <boost/program_options/detail/convert.hpp>
#endif

#if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042))
#include <istream> // std::getline
#endif

#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/shared_ptr.hpp>

//
// This class is just a clone of boost::program_options::detail::common_config_file_iterator
// Except that following option is allowed:
//
//  [section]
//    value1
//    value2
//    value3
//
// Means section=value1, section=value2, section=value3
//
class xpt_common_config_file_iterator :
    public boost::eof_iterator<xpt_common_config_file_iterator, boost::program_options::option>
{
public:
    xpt_common_config_file_iterator()
    {
        found_eof();
    }

    xpt_common_config_file_iterator(
            const std::set<std::string>& allowed_options,
            bool allow_unregistered = false);

    virtual ~xpt_common_config_file_iterator()
    {
    }

public: // method required by eof_iterator
    void get();

protected: // stubs for derived classes

    // Obtains next line from the config file
    // Note: really, this design is a bit ugly
    // The most clean thing would be to pass 'line_iterator' to
    // constructor of this class, but to avoid templating this class
    // we'd need polymorphic iterator, which does not exist yet.
    virtual bool getline(std::string&)
    {
        return false;
    }

private:
    // Adds another allowed option. If the 'name' ends with
    // '*', then all options with the same prefix are
    // allowed. For example, if 'name' is 'foo*', then 'foo1' and
    // 'foo_bar' are allowed.
    void add_option(const char* name);

    // Returns true if 's' is a registered option name.
    bool allowed_option(const std::string& s) const;

    // That's probably too much data for iterator, since
    // it will be copied, but let's not bother for now.
    std::set<std::string> allowed_options;
    // Invariant: no element is prefix of other element.
    std::set<std::string> allowed_prefixes;
    std::string m_prefix;
    bool m_allow_unregistered;
};

template <typename charT>
class xpt_basic_config_file_iterator :
    public xpt_common_config_file_iterator
{
public:
    xpt_basic_config_file_iterator()
    {
        found_eof();
    }

    // Creates a config file parser for the specified stream.
    xpt_basic_config_file_iterator(
            std::basic_istream<charT>& is,
            const std::set<std::string>& allowed_options,
            bool allow_unregistered = false);

private: // base overrides
    bool getline(std::string&);

private: // internal data
    boost::shared_ptr<std::basic_istream<charT> > is;
};

typedef xpt_basic_config_file_iterator<char> config_file_iterator;
typedef xpt_basic_config_file_iterator<wchar_t> wconfig_file_iterator;

struct xpt_null_deleter
{
    void operator()(void const *) const
    {
    }
};

template <typename charT>
xpt_basic_config_file_iterator<charT>::xpt_basic_config_file_iterator(
        std::basic_istream<charT>& is,
        const std::set<std::string>& allowed_options,
        bool allow_unregistered) :
    xpt_common_config_file_iterator(allowed_options, allow_unregistered)
{
    this->is.reset(&is, xpt_null_deleter());
    get();
}

// Specializing this function for wchar_t causes problems on
// borland and vc7, as well as on metrowerks. On the first two
// I don't know a workaround, so make use of 'to_internal' to
// avoid specialization.
template <typename charT>
bool xpt_basic_config_file_iterator<charT>::getline(std::string& s)
{
    std::basic_string<charT> in;
    if (std::getline(*is, in)) {
        s = boost::program_options::to_internal(in);
        return true;
    } else {
        return false;
    }
}

// Specialization is needed to workaround getline bug on Comeau.
#if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303)) || \
        (defined(__sgi) && BOOST_WORKAROUND(_COMPILER_VERSION, BOOST_TESTED_AT(741)))
template <>
bool xpt_basic_config_file_iterator<wchar_t>::getline(std::string& s);
#endif

//
// Parse a config file.
//
template <typename CharT>
boost::program_options::basic_parsed_options<CharT>
xpt_parse_config_file(
        std::basic_istream<CharT>&,
        const boost::program_options::options_description&,
        bool allow_unregistered = false);

#endif // XPTPLUS_CONFIG_FILE_HPP
